<template>
  <div>
    <div v-if="!isStudent" class="handleBtnCont">
      <el-button size="mini" v-if="this.jobId == null" type="success" style="margin-right: 50px" @click="showZLUpload">知识点抽取</el-button>
      <el-button v-if="this.jobId == null" size="mini" type="primary" @click="createDir">添加知识点</el-button>
      <el-button size="mini" v-if="this.jobId == null" type="warning" @click="checkGraph">查看课程知识图谱</el-button>
      <!-- <el-button size="mini" type="danger" @click="batchDialog">批量设置</el-button> -->
    </div>

    <!--      :expand-row-keys="computedExpand"-->
    <el-tree v-if="this.jobId == null" draggable :data="treeData" :props="defaultProps" node-key="id" default-expand-all class="knowledge-tree">
      <template #default="{ node, data }">
        <div :class="['tree-node', data.type]">
          <!-- 节点内容 -->
          <div>{{ node.label }}</div>
          <div v-if="data.content" class="content-text">
            <el-tooltip class="item" effect="light" placement="top-start">
              <div slot="content" style="max-width: 70vw">
                {{ data.content }}
              </div>
              <div class="content-text">- {{ data.content }}</div>
            </el-tooltip>
          </div>
          <div></div>
          <div v-if="data.prerequisites" class="prerequisites"></div>

          <!-- 动态按钮 -->
          <span class="action-buttons">
            <!-- Chapter 节点：添加 SubChapter 按钮 -->
            <template v-if="data.type === 'chapter' && !isStudent">
              <el-tooltip content="添加子章节" placement="top">
                <el-button icon="el-icon-plus" type="text" @click.stop="addSubChapter(node, data)">
                  <!--                <el-icon><Plus /></el-icon>-->
                </el-button>
              </el-tooltip>
            </template>

            <!-- SubChapter 节点：添加 Point 和删除按钮 -->
            <template v-if="data.type === 'sub_chapter' && !isStudent">
              <el-tooltip content="添加知识点" placement="top">
                <el-button icon="el-icon-plus" type="text" size="mini" @click.stop="addPoint(node, data)"> </el-button>
              </el-tooltip>
              <el-tooltip content="删除子章节" placement="top">
                <el-button type="text" icon="el-icon-close" @click.stop="deleteSubChapter(node, data)"> </el-button>
              </el-tooltip>
            </template>

            <!-- Point 节点：修改和删除按钮 -->
            <template v-if="data.type === 'point' && !isStudent">
              <el-tooltip content="修改知识点" placement="top">
                <el-button icon="el-icon-edit-outline" type="text" @click.stop="editPoint(data)"> </el-button>
              </el-tooltip>
              <el-tooltip content="删除知识点" placement="top">
                <el-button icon="el-icon-close" type="text" size="medium" @click.stop="deletePoint(node, data)"> </el-button>
              </el-tooltip>
            </template>
          </span>
        </div>
      </template>
    </el-tree>
    <div v-else class="waiting-container">
      <div class="waiting-content">
        <div class="waiting-header">
          <i class="el-icon-loading"></i>
          <h2>知识点抽取队列中</h2>
        </div>
        <div class="waiting-position">
          <div class="progress-wrapper">
            <div class="position-info">
              <span class="position-number">{{ positionBeforeYou }}</span>
              <span class="position-text">位用户在您前面</span>
            </div>
          </div>
          <div style="display: flex; width: 100%; justify-content: center; align-items: center">
            <img src="@/assets/waitting.png" style="width: 40%; padding: 24px" />
          </div>

          <p class="waiting-time">预计等待时间: {{ estimatedTime }}</p>
        </div>
        <div class="waiting-tips">
          <h3>等待小贴士</h3>
          <div class="tip-content">
            <p>{{ currentTip }}</p>
            <div class="tip-controls">
              <span @click="prevTip" class="tip-nav"><i class="el-icon-arrow-left"></i></span>
              <span class="tip-indicator">{{ tipIndex + 1 }}/{{ tips.length }}</span>
              <span @click="nextTip" class="tip-nav"><i class="el-icon-arrow-right"></i></span>
            </div>
          </div>
        </div>

        <div class="waiting-footer">
          <el-button size="large" type="primary" @click="checkQueuePosition">刷新队列状态</el-button>
        </div>
      </div>
    </div>
    <course-file-dialog ref="childComponent" :visible.sync="dialogVisible" :excludes="excludes" @select="dialogSelect" />

    <el-dialog :visible.sync="batchVisible" :close-on-click-modal="false" title="批量赋值" width="500px">
      <div style="font-size: 12px; color: #ff3333; margin-bottom: 20px">*复选以批量改变值，不勾选维持现有数据</div>

      <el-form :model="batchForm">
        <el-form-item label="要求时长">
          <div style="display: flex; align-items: center">
            <el-checkbox v-model="batchForm.needLearnCk" style="margin-right: 20px" />
            <second-set v-model="batchForm.needLearn" />
          </div>
        </el-form-item>

        <el-form-item label="学完积分">
          <el-checkbox v-model="batchForm.pointsCk" style="margin-right: 20px" />
          <el-input-number v-model="batchForm.points" size="mini" />
        </el-form-item>
      </el-form>

      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="confirmBatch()">确 定</el-button>
      </div>
    </el-dialog>

    <el-dialog :visible.sync="catDialog" :close-on-press-escape="false" :close-on-click-modal="false" width="500px">
      <el-form v-model="importForm" label-position="left" label-width="80px">
        <el-form-item label="导入类目">
          <dic-catalog-tree v-model="importForm.catId" dic-code="course_file_catalog" />
        </el-form-item>

        <el-form-item label="导入方式">
          <el-radio v-model="importForm.type" :label="1">下级目录及课件</el-radio>
          <el-radio v-model="importForm.type" :label="2">本目录及其课件</el-radio>
        </el-form-item>
      </el-form>

      <div slot="footer">
        <el-button type="primary" @click="handleImport">确认导入</el-button>
      </div>
    </el-dialog>

    <el-dialog :close-on-click-modal="false" :visible.sync="previewVisible" title="文件预览" width="60%" @close="closePreview">
      <el-alert v-if="previewData.fileType === '33'" title="内容由ai数字人生成，请注意甄别内容" type="info"> </el-alert>
      <div style="overflow-x: hidden; overflow-y: auto">
        <div v-if="previewData.fileType === '33'">
          <video-player v-model="previewData.viewUrl" :ref-id="previewData.id" @loadError="vieoLoadError" />
        </div>
        <pdf-reader v-if="previewData.fileType === '11'" :src="previewData.viewUrl" />
        <pdf-reader v-if="previewData.fileType === '22'" :src="previewData.fileUrl" />
      </div>
    </el-dialog>
    <el-dialog :visible.sync="extrVisible" :close-on-click-modal="false" :before-close="closeExtr" title="知识点图谱" width="80%" style="min-width: 800px">
      <template #title>
        <div style="display: flex; justify-content: space-between">
          <div>{{ showGraphComponent ? '知识图谱' : '知识点抽取' }}</div>
        </div>
      </template>

      <div class="document-ipt-wrapper">
        <div v-show="!docLoading && !showGraphComponent">
          <div class="left">
            <el-alert :closable="false" title="* 试用期间仅生成10页内容的知识点, 请上传word/pdf课程教材，且不超过 150MB" type="warning" />
            <el-upload
              v-loading="docExtrLoading"
              class="upload"
              ref="docUpload"
              drag
              :data="uploadParams"
              :action="`${extractURI}/upload`"
              :limit="1"
              :on-exceed="handleDocExceed"
              :on-success="handleDocSuccess"
              :on-error="handleDocError"
              :on-change="handleDocFileChange"
              :on-remove="handleDocRemove"
              :before-upload="handleDocBeforeUpload"
              accept=".pdf,.doc,.docx"
            >
              <div class="el-upload-area">
                <div class="loading" v-show="docBtnLoading">
                  <img src="@/assets/loading2.gif" />
                  <span>文件正在上传</span>
                </div>
                <i class="el-icon-upload" style="margin-top: 40px"></i>
                <div class="el-upload__text" style="text-align: center; font-size: 16px; font-weight: bold">将文件拖拽到此处，或 <em>点击上传</em></div>
              </div>
            </el-upload>
            <div class="buttons-container">
              <el-button style="width: 100%" size="large" type="primary" class="documentIpt_btn" @click.stop="submittextStructure" :disabled="docExtrLoading">知识点生成</el-button>
            </div>
          </div>
        </div>

        <!-- 知识图谱组件 -->
        <div v-if="showGraphComponent" style="width: 100%; height: 100%; display: flex; align-items: center; flex-direction: column">
          <div ref="chart_container" class="content_container" v-loading="graphLoading" @dblclick="fullScreen">
            <div ref="chartContainer" class="tree-chart" style="width: 100%; height: 100%; overflow: hidden"></div>
          </div>
          <div class="buttons-container">
            <el-button size="large" type="primary" class="documentIpt_btn left_btn" @click="downloadImage">下载图谱</el-button>
            <el-button size="large" type="primary" class="documentIpt_btn right_btn" @click="regenerateGraph">重新生成</el-button>
          </div>
        </div>
        <div v-show="docLoading">
          <div class="loadingleft">
            <img src="@/assets/rerun.gif" alt="Loading Image" style="width: 300px; height: 200px; margin-top: 190px; margin-left: 130px" />
            <div class="progress-container">
              <div class="progress-bar" :style="{ width: docProgress + '%' }">
                <span style="margin-left: 420px; color: white">{{ docProgress }}%</span>
              </div>
              <p class="progress-text">{{ docProgressText }}</p>
            </div>
          </div>
          <div class="loadingright">
            <div class="image-text-container" style="margin-top: 80px; width: 800px; padding-left: 50px">
              <h1 style="font-size: 32px; text-align: center">课程知识点</h1>
              <p style="font-size: 26px; padding: 50px 100px 30px 50px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ docCurrentText }}</p>
              <img :src="loadingImages[docCurrentIndex]" alt="Loading Image" style="width: 650px; height: 350px; padding-left: 70px" />
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { uuid } from 'vue-uuid'
import { mapGetters } from 'vuex'
import axios from 'axios'
import * as echarts from 'echarts'
import html2canvas from 'html2canvas'
import CourseFileDialog from '@/components/CourseFileDialog'
import DicCatalogTree from '@/components/DicTreeSelect'
import { fetchFile } from '@/api/course/file'
import { getCourseKnowledge } from '@/api/course/course'
import { updateCourseKnowledge, resetGraph } from '@/api/course/course'
import SecondSet from '@/components/SecondSet'
import VideoPlayer from '@/components/VideoPlayer'
import PdfReader from '@/components/PdfReader'
import { saveDataGetId } from '@/api/course/course'
import DigitalHumanGennerate from '@/components/DigitalHumanGenerate'

export default {
  name: 'pointList',
  components: {
    SecondSet,
    DicCatalogTree,
    CourseFileDialog,
    PdfReader,
    VideoPlayer,
    DigitalHumanGennerate
  },
  props: {
    value: {
      type: Array,
      default: () => []
    },
    isDigitalHuman: {
      type: Boolean,
      default: false
    },
    isStudent: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      changeNode: null,
      treeData: [],
      defaultProps: {
        children: 'children',
        label: 'label'
      },
      dialogVisible: false,
      excludes: [],
      jobId: null,
      jobQueryTimer: null,
      positionBeforeYou: 0,
      pointList: [
        {
          id: 1,
          chapter: '第一章：基础概念',
          sub_chapters: [
            {
              id: 11,
              sub_chapter: '1.1 数据结构入门',
              points: [
                { point: '定义', content: '数据结构是组织和存储数据的系统方法' },
                { point: '应用场景', content: '数据库索引设计、操作系统文件管理' }
              ]
            }
          ]
        },
        {
          id: 2,
          chapter: '第二章：线性结构',
          sub_chapters: [
            {
              id: 21,
              sub_chapter: '2.1 数组与链表',
              points: [
                { point: '静态数组', content: '连续内存分配，支持随机访问' },
                { point: '动态数组', content: '自动扩容的数组实现' }
              ]
            }
          ]
        }
      ],
      liveData: {},
      batchVisible: false,
      batchForm: {
        needLearn: 0,
        points: 0,
        faceStart: false,
        faceInterval: 0
      },
      catDialog: false,
      catId: null,
      importForm: {
        excludes: []
      },
      previewVisible: false,
      previewData: {},
      extrVisible: false,
      windowHeight: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
      postForm: {},
      uploadParams: {},
      iframeBaseUrl: '',
      iframeUrl: '',
      tempId: `temp_${uuid.v4()}`,
      docLoading: false,
      docExtrLoading: false,
      docBtnLoading: false,
      docFlag: false,
      docFileId: null,
      docUploadSuccessReturnId: null,
      docProgress: 0,
      docCurrentIndex: 0,
      docCurrentText: '',
      docProgressText: '',
      timeStamp: Date.now(),
      loadingImages: [require('@/assets/gr.png'), require('@/assets/loadingpicture/loading2.jpg'), require('@/assets/loadingpicture/loading3.jpg'), require('@/assets/loadingpicture/loading4.jpg'), require('@/assets/loadingpicture/load5.jpg')],
      extractURI: 'http://10.21.22.107:3000',
      // 图谱组件相关
      showGraphComponent: false,
      graphLoading: false,
      chartInstance: null,
      // 等待知识点抽取相关
      tips: ['请耐心等待，系统正在为您处理知识点抽取。', '您可以在此期间浏览其他课程内容。', '知识点抽取和传入内容数量强相关，为了保证生成速度，请尽量降低传入的资料大小', '感谢您的耐心，知识点抽取是为了提供更优质的学习体验。'],
      tipIndex: 0,
      tipTimer: null, // 用于存储定时器ID
      progressColor: '#409EFF'
    }
  },
  computed: {
    computedExpand() {
      const arr = []
      this.pointList.map(item => {
        if (item.fileList && item.fileList.length > 0) {
          arr.push(item.id)
        }
      })
      return arr
    },
    ...mapGetters(['zlBaseUrl']),
    // 等待界面相关计算属性
    estimatedTime() {
      // 根据队列位置估算等待时间
      if (this.positionBeforeYou === 0) {
        return '10-20分钟'
      } else if (this.positionBeforeYou <= 3) {
        return '1-2小时'
      } else if (this.positionBeforeYou <= 10) {
        return '5-8小时'
      } else {
        return '1天以上'
      }
    },
    currentTip() {
      // 获取当前显示的提示
      return this.tips[this.tipIndex]
    }
  },
  watch: {
    value: {
      handler(val) {
        this.pointList = val
        if (this.pointList && this.pointList.length) {
          this.treeData = JSON.parse(this.pointList[0].desKnowledge)
        }
      },
      deep: true
    },

    // 检测查询变化
    pointList: {
      handler(val) {
        this.$emit('input', val)
        this.processExcludes()
      },
      deep: true
    },
    treeData: {
      handler(val) {
        console.log('tcd')
        this.pointList[0].desKnowledge = JSON.stringify(this.treeData)
        console.log(this.pointList)
        this.$emit('updateList', this.pointList)
      },
      deep: true
    }
  },
  created() {
    this.iframeBaseUrl = `${this.zlBaseUrl}/#/navigation/documentIpt`
    this.pointList = this.value
    if (this.pointList.length) {
      this.treeData = JSON.parse(this.pointList[0].desKnowledge)
    }
    /*this.treeData=this.originalData
    console.log(JSON.stringify(this.treeData))*/
  },
  mounted() {
    window.addEventListener('message', res => {
      if (res.data && res.data instanceof Object) {
        if (res.data.closeIframe) {
          this.closeExtr()
        }
      }
    })

    this.startTipCarousel()
    // 从URL中获取课程ID
    this.getCourseIdFromUrl()
  },
  beforeDestroy() {
    // 组件销毁前清除定时器
    this.clearTipCarousel()
  },
  methods: {
    // 启动提示轮播
    startTipCarousel() {
      this.clearTipCarousel() // 先清除可能存在的定时器
      this.tipTimer = setInterval(() => {
        this.nextTip()
      }, 3000) // 每3秒切换一次提示
    },

    // 清除提示轮播定时器
    clearTipCarousel() {
      if (this.tipTimer) {
        clearInterval(this.tipTimer)
        this.tipTimer = null
      }
    },

    checkJobOnce() {
      try {
        console.log(this.jobId)
        axios
          .post(`${this.extractURI}/check`, {
            job_id: this.jobId
          })
          .then(res => {
            console.log(res, 'essss')
            if (res.data.status == 'finished') {
              clearInterval(this.jobQueryTimer)
              localStorage.removeItem(this.postForm.id)
              this.jobId = null
              this.jobQueryTimer = null
              this.$message.success('知识点抽取已完成！')
              
              // 重新获取课程知识点数据
              this.refreshKnowledgeData()
            } else {
              this.positionBeforeYou = res.data.position_before_you
            }
          })
          .catch(error => {
            clearInterval(this.jobQueryTimer)
            localStorage.removeItem(this.postForm.id)
            this.jobId = null
            this.jobQueryTimer = null
            this.$message.error('检查任务状态失败')
          })
      } catch (error) {
        clearInterval(this.jobQueryTimer)
        localStorage.removeItem(this.postForm.id)
        this.jobId = null
        this.jobQueryTimer = null
        console.error('检查任务状态异常:', error)
      }
    },
    // 检测任务状态，15s轮询一次
    checkJob() {
      if (this.jobId) {
        this.checkJobOnce()
        this.jobQueryTimer = setInterval(this.checkJobOnce, 1000 * 15)
      } else {
        return false
      }
      return true
    },

    // 获取URL中的课程ID
    getCourseIdFromUrl() {
      const url = window.location.href
      const match = url.match(/\/update\/(\d+)/)
      if (match && match[1]) {
        this.postForm.id = match[1]
        console.log('从URL中获取课程ID：', this.postForm.id)
      }
      this.uploadParams = {
        course_id: this.postForm.id,
        time_stamp: this.timeStamp
      }
      if (localStorage.getItem(this.postForm.id)) {
        this.jobId = localStorage.getItem(this.postForm.id)
        this.checkJob()
      }
    },
    // 数据转换
    transformData(data) {
      const timestamp = Date.now()
      return data.map((chapter, cIndex) => ({
        id: `chapter_${timestamp}_${cIndex}`,
        label: chapter.chapter,
        type: 'chapter',
        children: chapter.sub_chapters.map((sub, sIndex) => ({
          id: `sub_${timestamp}_${cIndex}_${sIndex}`,
          label: sub.sub_chapter,
          type: 'sub_chapter',
          children: sub.points.map((point, pIndex) => ({
            id: `point_${timestamp}_${cIndex}_${sIndex}_${pIndex}`,
            label: point.point,
            type: 'point',
            content: point.content,
            prerequisites: point.prerequisites
          }))
        }))
      }))
    },

    // 添加 SubChapter
    addSubChapter(node, data) {
      this.$prompt('请输入子章节名称', '添加子章节', {
        inputPlaceholder: '例如：1.2 算法基础'
      }).then(({ value }) => {
        if (!value) return
        const newSubChapter = {
          sub_chapter: value,
          points: []
        }
        if (!data.children) {
          this.$set(data, 'children', [])
        }
        data.children.push({
          id: `sub_${Date.now()}`,
          label: newSubChapter.sub_chapter,
          type: 'sub_chapter',
          children: []
        })
        updateCourseKnowledge(this.pointList[0].courseId, JSON.stringify(this.treeData))
          .then(() => {
            this.$message.success('添加成功')
            resetGraph(this.pointList[0].courseId)
          })
          .catch(() => {
            this.$message.error('添加失败')
          })
      })
    },

    // 添加 Point
    // 添加 Point
    addPoint(node, data) {
      this.$msgbox({
        title: '添加知识点',
        message: `
      <div>
        <div class="el-form-item">
          <div class="el-form-item__label">知识点标题</div>
          <input id="new-knowledge-label" class="el-input__inner" placeholder="例如：时间复杂度" />
        </div>
        <div class="el-form-item" style="margin-top: 10px;">
          <div class="el-form-item__label">知识点内容</div>
          <textarea id="new-knowledge-content" class="el-textarea__inner" rows="4" placeholder="请输入知识点内容">请输入内容</textarea>
        </div>
      </div>
    `,
        dangerouslyUseHTMLString: true,
        showCancelButton: true,
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        beforeClose: (action, instance, done) => {
          if (action === 'confirm') {
            const newLabel = document.getElementById('new-knowledge-label').value
            const newContent = document.getElementById('new-knowledge-content').value

            if (!newLabel) {
              this.$message.error('请输入知识点标题')
              return
            }

            const newPoint = {
              point: newLabel,
              content: newContent || '请输入内容',
              prerequisites: []
            }

            if (!data.children) {
              this.$set(data, 'children', [])
            }

            data.children.push({
              id: `point_${Date.now()}`,
              label: newPoint.point,
              type: 'point',
              content: newPoint.content,
              prerequisites: newPoint.prerequisites
            })

            updateCourseKnowledge(this.pointList[0].courseId, JSON.stringify(this.treeData))
              .then(() => {
                this.$message.success('添加成功')
                resetGraph(this.pointList[0].courseId)
                done()
              })
              .catch(() => {
                this.$message.error('添加失败')
                done()
              })
          } else {
            done()
          }
        }
      })
    },

    // 删除 Point
    deletePoint(node, data) {
      const parent = node.parent
      const children = parent.data.children || parent.data
      const index = children.findIndex(d => d.id === data.id)
      children.splice(index, 1)

      updateCourseKnowledge(this.pointList[0].courseId, JSON.stringify(this.treeData))
        .then(() => {
          this.$message.success('删除成功')
          resetGraph(this.pointList[0].courseId)
        })
        .catch(() => {
          this.$message.error('删除失败')
        })
    },

    // 删除 SubChapter
    deleteSubChapter(node, data) {
      const parent = node.parent
      const children = parent.data.children || parent.data
      const index = children.findIndex(d => d.id === data.id)
      children.splice(index, 1)
      updateCourseKnowledge(this.pointList[0].courseId, JSON.stringify(this.treeData))
        .then(() => {
          this.$message.success('删除成功')
          resetGraph(this.pointList[0].courseId)
        })
        .catch(() => {
          this.$message.error('删除失败')
        })
    },

    // 修改 Point
    editPoint(data) {
      this.changeNode = data
      let tempLabel = data.label
      let tempContent = data.content || ''

      this.$msgbox({
        title: '修改知识点',
        message: `
      <div>
        <div class="el-form-item">
          <div class="el-form-item__label">知识点标题</div>
          <input id="knowledge-label" class="el-input__inner" value="${tempLabel}" placeholder="请输入知识点标题" />
        </div>
        <div class="el-form-item" style="margin-top: 10px;">
          <div class="el-form-item__label">知识点内容</div>
          <textarea id="knowledge-content" class="el-textarea__inner" rows="4" placeholder="请输入知识点内容">${tempContent}</textarea>
        </div>
      </div>
    `,
        dangerouslyUseHTMLString: true,
        showCancelButton: true,
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        beforeClose: (action, instance, done) => {
          if (action === 'confirm') {
            const newLabel = document.getElementById('knowledge-label').value
            const newContent = document.getElementById('knowledge-content').value

            data.label = newLabel
            data.content = newContent

            // 更新知识点
            updateCourseKnowledge(this.pointList[0].courseId, JSON.stringify(this.treeData))
              .then(() => {
                this.$message.success('修改成功')
                resetGraph(this.pointList[0].courseId)
                done()
              })
              .catch(() => {
                this.$message.error('修改失败')
                done()
              })
          } else {
            done()
          }
        }
      })
    },
    checkGraph() {
      if (this.pointList.length <= 0) {
        this.$message.error('请先进行知识点抽取')
        return
      } else {
        // 直接在对话框中展示知识图谱，而不是通过iframe
        this.extrVisible = true
        this.showGraphComponent = true
        // 确保在DOM更新后初始化图表
        this.$nextTick(() => {
          // 图表会在组件挂载时自动加载数据和初始化
          this.fetchKnowledgeGraphData(this.postForm.id)
        })
      }
    },

    async showZLUpload() {
      // 切换打开为知识点抽取
      this.showGraphComponent = false
      console.log('showZLUpload', this.postForm.id)

      if (!this.postForm.id) {
        this.postForm.id = this.tempId
      }
      this.extrVisible = true
    },
    closeExtr() {
      this.extrVisible = false
    },

    // 移除目录
    removeDir(index) {
      this.pointList.splice(index, 1)
    },

    // 移除文件
    removeFile(fileList, index2) {
      fileList.splice(index2, 1)
    },

    createDir() {
      this.$prompt('请输入知识点名称', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消'
      }).then(({ value }) => {
        for (let i = 0; i < this.pointList.length; i++) {
          if (this.pointList[i].title === value) {
            this.$message.error(`知识点名称：${value}重复了，请换一个！`)
            return
          }
        }

        this.pointList.push({ title: value, fileList: [] })
      })
    },

    batchAddDir(data) {
      if (data.length > 0) {
        /* const newArr = []
        data.map(oItem => {
          oItem.content.map((item, index) => {
            if (this.pointList.filter(dItem => dItem.title == item.point).length>0) {
              return
            }
            newArr.push({ title: item.point, fileList: [], id: parseInt(Math.random() * 100000) + String((index + 1)) })
          })
        })

        this.pointList = this.pointList.concat(newArr)*/
        this.treeData = this.transformData(data)
      }
    },

    fileDialog(index) {
      console.log('childComponent')

      this.curIndex = index
      this.dialogVisible = true
      this.$refs.childComponent.handleAdd()
    },

    dialogSelect(objs) {
      if (objs != null && objs.length > 0) {
        for (let i = 0; i < objs.length; i++) {
          this.pointList[this.curIndex].fileList.push({
            fileId: objs[i].id,
            fileUrl: objs[i].fileUrl,
            title: objs[i].title,
            fileType: objs[i].fileType,
            fileType_dictText: objs[i].fileType_dictText,
            needLearn: objs[i].duration,
            points: 0
          })
        }
      }

      console.log('objs', objs)
    },

    sortDownDir(dirIndex) {
      const newIndex = dirIndex + 1
      const targetIndex = this.pointList.splice(dirIndex, 1)[0]
      this.pointList.splice(newIndex, 0, targetIndex)
    },

    sortUpDir(dirIndex) {
      const newIndex = dirIndex - 1
      const targetIndex = this.pointList.splice(dirIndex, 1)[0]
      this.pointList.splice(newIndex, 0, targetIndex)
    },

    sortDownFile(fileList, fileIndex) {
      const newIndex = fileIndex + 1
      const targetIndex = fileList.splice(fileIndex, 1)[0]
      fileList.splice(newIndex, 0, targetIndex)
    },

    sortUpFile(fileList, fileIndex) {
      const newIndex = fileIndex - 1
      const targetIndex = fileList.splice(fileIndex, 1)[0]
      fileList.splice(newIndex, 0, targetIndex)
    },

    // 处理已在列表的文件，避免重复加入
    processExcludes() {
      const ids = []
      for (let i = 0; i < this.pointList.length; i++) {
        const files = this.pointList[i].fileList
        if (!files || files.length <= 0) {
          continue
        }
        for (let j = 0; j < files.length; j++) {
          ids.push(files[j].fileId)
        }
      }

      this.excludes = ids
    },

    batchDialog() {
      this.batchVisible = true
    },

    confirmBatch() {
      this.batchVisible = false

      for (let i = 0; i < this.pointList.length; i++) {
        const fileList = this.pointList[i].fileList
        if (fileList.length > 0) {
          for (let j = 0; j < fileList.length; j++) {
            if (this.batchForm.needLearnCk) {
              fileList[j].needLearn = this.batchForm.needLearn
            }

            if (this.batchForm.pointsCk) {
              fileList[j].points = this.batchForm.points
            }
          }
        }
      }

      this.$forceUpdate()
    },

    importCat() {
      this.catDialog = true
    },

    handleImport() {
      // 根据分类找到结构
      this.catDialog = false

      // 批量添加目录
      this.importForm.excludes = this.excludes

      fetchFile(this.importForm).then(res => {
        for (let i = 0; i < res.data.length; i++) {
          const dir = res.data[i]
          this.pointList.push(dir)
        }
      })
    },

    handlePreview(data) {
      console.log('pv', data)
      this.previewVisible = true
      this.previewData.fileType = data.fileType
      this.previewData.viewUrl = data.viewUrl || data.fileUrl
      this.previewData.fileUrl = data.fileUrl
    },

    closePreview() {
      this.previewData = {}
    },

    vieoLoadError(e) {
      this.previewVisible = false
      this.$message({
        type: 'error',
        message: '授课视频正在生成中，暂不能预览',
        duration: 3000
      })
    },
    handleDocExceed(files) {
      this.docBtnLoading = false
      this.$refs.docUpload.clearFiles()
      const file = files[0]
      this.$refs.docUpload.handleStart(file)
    },
    handleDocRemove() {
      // 清除文件数据
    },
    handleDocBeforeUpload(file) {
      const isLt2M = file.size / 1024 / 1024 < 150
      if (!isLt2M) {
        this.$message.error('上传的文件大小不能超过 150MB!')
      }
      return isLt2M
    },
    handleDocSuccess(response, file, fileList) {
      this.docFlag = true
      this.docFileId = response.file_id
      this.docBtnLoading = false
      this.$message({
        message: '文件上传成功！',
        type: 'success',
        duration: 2000
      })
      this.docUploadSuccessReturnId = response.id
    },
    handleDocError(err, file, fileList) {
      this.docBtnLoading = false
      this.$message({
        message: '文件上传失败！',
        type: 'error',
        duration: 2000
      })
    },
    handleDocFileChange() {
      // 文件变更时的处理
    },
    submittextStructure() {
      if (this.docFlag && this.docUploadSuccessReturnId) {
        this.docExtrLoading = true
        const formData = new FormData()
        formData.append('id', this.docUploadSuccessReturnId)
        formData.append('course_id', this.postForm.id)
        formData.append('time_stamp', this.timeStamp)

        axios
          .post(`${this.extractURI}/extract`, formData, {
            timeout: 30 * 60 * 1000
          })
          .then(res => {
            this.docExtrLoading = false
            if (res.status == 202) {
              localStorage.setItem('chap', JSON.stringify(res.data))
              localStorage.setItem(this.postForm.id, res.data.job_id)
              this.jobId = res.data.job_id
              this.checkJob()
              this.$message({
                message: '知识点开始抽取',
                type: 'success',
                duration: 2000
              })
              this.$store.commit('SET_CHAP', JSON.stringify(res.data))
              if (res.data && res.data.length > 0) {
                this.batchAddDir(res.data)
              }
              this.docFlag = false
              this.$refs.docUpload.clearFiles()
              this.closeExtr()
            } else {
              this.docExtrLoading = false
              this.$message.error(`${res.status}接口响应错误`)
            }
          })
          .catch(error => {
            this.docExtrLoading = false
            this.$message.error('知识点生成失败！')
          })
        return
      } else {
        this.$message.error('请先上传文件')
      }
    },

    // 获取后端知识图谱的数据
    async fetchKnowledgeGraphData(courseId) {
      this.graphLoading = true
      try {
        const response = await axios.post(`http://111.63.183.17:5000/Graphdata?course_id=${courseId}`, {})
        // 处理节点id为字符串类型
        const processedData = this.processGraphData(this.processNodeIds(response.data))
        this.$nextTick(() => {
          this.initChart(processedData)
          this.graphLoading = false
        })
      } catch (error) {
        console.error('数据获取失败:', error)
        this.$message.error('获取知识图谱数据失败')
        this.graphLoading = false
      }
    },

    // 重新生成知识图谱
    async regenerateGraph() {
      this.graphLoading = true
      try {
        const response = await axios.post(`http://111.63.183.17:5000/ResetGraph?course_id=${this.postForm.id}`, {})
        // 处理节点id为字符串类型
        const processedData = this.processGraphData(this.processNodeIds(response.data))
        this.$nextTick(() => {
          this.initChart(processedData)
          this.graphLoading = false
        })
      } catch (error) {
        console.error('数据获取失败:', error)
        this.$message.error('重新生成知识图谱失败')
        this.graphLoading = false
      }
    },

    // 处理节点id为字符串类型
    processNodeIds(data) {
      data.nodes.forEach(node => {
        node.id = String(node.id) // 将id转换为字符串类型
      })
      data.links.forEach(link => {
        link.source = String(link.source) // 将id转换为字符串类型
        link.target = String(link.target) // 将id转换为字符串类型
      })
      return data
    },

    // 处理原始数据代码
    processGraphData(data) {
      const nodes = []
      const links = []
      console.log(data)
      const nodeMap = new Map()
      // 节点处理
      data.nodes.forEach(node => {
        const processedNode = {
          id: String(node.id),
          name: node.name,
          description: node.description,
          level: 0,
          children: [],
          isCollapsed: false,
          symbolSize: 30,
          itemStyle: { color: '#5470c6' }
        }
        nodes.push(processedNode)
        nodeMap.set(String(node.id), processedNode)
      })

      // 通过Map精确获取节点引用
      data.links.forEach(link => {
        // 强制转换source/target为字符串类型
        const sourceId = String(link.source)
        const targetId = String(link.target)

        // 从Map中获取实际节点对象（而非临时变量）
        const sourceNode = nodeMap.get(sourceId)
        const targetNode = nodeMap.get(targetId)

        if (sourceNode && targetNode) {
          sourceNode.children.push(targetNode) // 直接操作Map中的引用
          this.updateNodeLevels(targetNode, sourceNode.level + 1) // 处理层级
          links.push({ source: sourceId, target: targetId })
        } else {
          console.warn(`Invalid link: source=${sourceId}或target=${targetId}未找到`)
        }
      })

      // 根据层级设置不同颜色
      const colorMap = ['#5470c6', '#91cc75', '#f1c232', '#ff7f0e', '#d62728'] // 你可以根据需要调整颜色

      nodes.forEach(node => {
        node.symbolSize = 30 - node.level * 5 // 随着层级增加，节点变小
        node.itemStyle.color = colorMap[node.level] || '#b0b0b0' // 设置不同层级的颜色，如果超出指定层级则使用默认颜色
      })

      return { nodes, links }
    },

    // 工具函数，用于 processGraphData 中的节点level更新
    updateNodeLevels(node, level) {
      if (node.level > level) return
      node.level = level
      if (node.children) {
        node.children.forEach(child => {
          this.updateNodeLevels(child, level + 1)
        })
      }
    },

    // 初始化图表
    initChart(graphData) {
      console.log('chart', graphData.nodes)
      if (!this.$refs.chartContainer) return
      this.chartInstance = echarts.init(this.$refs.chartContainer)

      const option = {
        tooltip: {
          formatter: params => {
            if (params.dataType === 'node') {
              return `${params.data.name}<br/>${params.data.description || '暂无详细描述'}`
            }
          }
        },
        series: [
          {
            type: 'graph',
            layout: 'force',
            data: graphData.nodes,
            links: graphData.links,
            roam: true, // 支持图表拖动
            focusNodeAdjacency: true, // 鼠标悬停时高亮邻居节点
            label: {
              show: true,
              position: 'right',
              formatter: '{b}' // 显示节点名称
            },
            force: {
              repulsion: 100,
              gravity: 0.1,
              edgeLength: 50
            },
            emphasis: {
              focus: 'adjacency',
              label: {
                show: true
              }
            },
            // 设置链接的样式
            lineStyle: {
              color: '#ccc',
              width: 3
            },
            draggable: true // 开启节点拖拽功能
          }
        ]
      }

      this.chartInstance.setOption(option)

      // 窗口自适应
      window.addEventListener('resize', () => this.chartInstance && this.chartInstance.resize())

      // 事件监听：点击节点折叠子节点
      this.chartInstance.on('click', event => {
        if (event.dataType === 'node') {
          const clickedNodeId = event.data.id
          this.toggleCollapse(clickedNodeId, graphData)
        }
      })
    },

    // 折叠/展开节点（仅折叠子节点）
    toggleCollapse(nodeId, graphData) {
      const node = graphData.nodes.find(n => n.id === nodeId)

      if (node) {
        node.isCollapsed = !node.isCollapsed

        // 更新节点及其连接关系
        if (node.isCollapsed) {
          // 折叠时，隐藏该节点的所有子节点，并删除与子节点的连接
          this.collapseNodeChildren(node, graphData)
        } else {
          // 展开时，恢复该节点的所有子节点，并恢复连接
          this.expandNodeChildren(node, graphData)
        }

        // 更新图谱数据
        this.updateGraphData(graphData)
      }
    },

    // 折叠节点的子节点及其连接
    collapseNodeChildren(node, graphData) {
      if (!node.children || node.children.length === 0) return

      // 递归折叠每个子节点
      for (let i = 0; i < node.children.length; i++) {
        const child = node.children[i]
        // 删除子节点
        graphData.nodes = graphData.nodes.filter(n => n.id !== child.id)
        // 删除与子节点的链接
        graphData.links = graphData.links.filter(link => link.source !== child.id && link.target !== child.id)
        this.collapseNodeChildren(child, graphData) // 递归折叠子节点
      }
    },

    // 展开节点的子节点及其连接
    expandNodeChildren(node, graphData) {
      // 恢复该节点
      if (!graphData.nodes.find(n => n.id === node.id)) {
        graphData.nodes.push(node)
      }

      // 恢复该节点的子节点及其连接
      if (node.children && node.children.length > 0) {
        for (let i = 0; i < node.children.length; i++) {
          const child = node.children[i]
          // 恢复父子节点的连接
          if (!graphData.links.some(link => link.source === node.id && link.target === child.id)) {
            graphData.links.push({ source: node.id, target: child.id })
          }

          // 递归展开子节点
          this.expandNodeChildren(child, graphData)
        }
      }
    },

    // 更新图谱数据
    updateGraphData(graphData) {
      const updatedNodes = []
      const updatedLinks = []

      // 更新节点和链接
      graphData.nodes.forEach(node => {
        if (!node.isCollapsed) {
          updatedNodes.push(node)
        }

        // 处理子节点的链接
        if (Array.isArray(node.children)) {
          for (let i = 0; i < node.children.length; i++) {
            const child = node.children[i]
            if (!child.isCollapsed) {
              updatedLinks.push({
                source: node.id,
                target: child.id
              })
            } else {
              // 保留折叠节点和父节点的链接
              updatedLinks.push({
                source: node.id,
                target: child.id
              })
            }
          }
        }
      })
      // 在更新图表时，保留折叠的节点
      graphData.nodes.forEach(node => {
        if (node.isCollapsed && !updatedNodes.find(n => n.id === node.id)) {
          updatedNodes.push(node) // 将折叠的节点加入
        }
      })

      // 更新图表
      this.chartInstance.setOption({
        series: [
          {
            data: updatedNodes,
            links: updatedLinks
          }
        ]
      })
    },

    // 全屏显示图谱
    fullScreen() {
      const container = this.$refs.chart_container
      if (container) {
        if (container.requestFullscreen) {
          container.requestFullscreen()
        } else if (container.webkitRequestFullscreen) {
          // 兼容 WebKit 浏览器
          container.webkitRequestFullscreen()
        } else if (container.mozRequestFullScreen) {
          // 兼容 Firefox
          container.mozRequestFullScreen()
        }
      }
    },

    // 下载图谱图片
    async downloadImage() {
      try {
        const canvas = await html2canvas(this.$refs.chart_container)
        const image = canvas.toDataURL('image/png')
        const link = document.createElement('a')
        link.href = image
        link.download = 'knowledge_graph.png'
        link.click()
      } catch (error) {
        console.error('下载图谱失败:', error)
        this.$message.error('下载图谱失败')
      }
    },

    // 等待界面相关方法
    prevTip() {
      this.tipIndex = (this.tipIndex - 1 + this.tips.length) % this.tips.length
      this.startTipCarousel() // 重置定时器
    },

    nextTip() {
      this.tipIndex = (this.tipIndex + 1) % this.tips.length
      this.startTipCarousel() // 重置定时器
    },

    // 刷新队列状态
    checkQueuePosition() {
      if (!this.jobId) return

      this.$message({
        type: 'info',
        message: '正在刷新队列状态...',
        duration: 1500
      })

      axios
        .post(`${this.extractURI}/check`, {
          job_id: this.jobId
        })
        .then(res => {
          if (res.data && res.data.position_before_you !== undefined) {
            this.positionBeforeYou = res.data.position_before_you

            if (res.data.status === 'finished') {
              clearInterval(this.jobQueryTimer)
              localStorage.removeItem(this.postForm.id)
              this.jobId = null
              this.jobQueryTimer = null
              this.$message.success('知识点抽取已完成')
              // 重新获取课程知识点数据
              this.refreshKnowledgeData()
            } else {
              this.$message.success('队列状态已更新')
            }
          }
        })
        .catch(error => {
          console.error('刷新队列状态失败:', error)
          this.$message.error('刷新队列状态失败')
        })
    },

    // 重新获取课程知识点数据
    async refreshKnowledgeData() {
      try {
        // 显示加载状态
        this.$message({
          type: 'info',
          message: '正在获取最新的知识点数据...',
          duration: 1500
        })

        // 重新获取课程知识点数据
        const response = await getCourseKnowledge(this.postForm.id)
        if (response.data && response.data.point) {
          const newTreeData = JSON.parse(response.data.point)
          
          // 更新知识点树数据
          this.treeData = newTreeData
          
          // 更新 pointList 数据
          if (this.pointList.length > 0) {
            this.pointList[0].desKnowledge = response.data.point
            this.$emit('updateList', this.pointList)
          }

          this.$message.success('知识点数据已更新！')
          
          // 如果有知识图谱组件显示，重新加载图谱数据
          if (this.showGraphComponent) {
            this.$nextTick(() => {
              this.fetchKnowledgeGraphData(this.postForm.id)
            })
          }
        } else {
          this.$message.warning('未获取到新的知识点数据')
        }
      } catch (error) {
        console.error('获取知识点数据失败:', error)
        this.$message.error('获取知识点数据失败，请手动刷新页面')
        
        // 如果API调用失败，提供手动刷新选项
        this.$confirm('数据获取失败，是否手动刷新页面？', '提示', {
          confirmButtonText: '刷新页面',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          window.location.reload()
        }).catch(() => {
          // 用户取消刷新
        })
      }
    }

    // 取消知识点抽取
  }
}
</script>

<style scoped lang="scss">
.knowledge-tree {
  ::v-deep .el-tree-node {
    margin: 12px 0;
    position: relative; // 确保层级独立
    z-index: 1; // 防止下方行覆盖上方行

    // 调整每一行的行高
    .el-tree-node__content {
      height: auto; // 设置行高
      display: flex;
      align-items: center;
    }

    // 章节样式（一级标题）

    .chapter {
      /*background-color: #f0f8ff; // 淡蓝色背景*/
      border-radius: 8px;
      font-size: 18px;
      font-weight: 800;
      color: darkblue;
    }

    // 子章节样式（二级标题）
    .sub_chapter {
      font-size: 16px;
      font-weight: 800;
      color: #34495e;
    }

    // 知识点样式（三级标题）
    .point {
      font-size: 14px;
      font-weight: 400;
      color: #7f8c8d;

      .content-text {
        font-size: 13px;
        color: #95a5a6;
      }
    }

    // 先决条件标签样式
    .prerequisites {
      margin-left: 10px;

      .el-tag {
        margin-right: 5px;
        transform: scale(0.9);
      }
    }
  }

  .tree-node {
    display: flex;
    align-items: center;
    width: 100%;
    height: 40px;

    .content-text {
      margin-left: 10px;
      font-size: 13px;
      color: #666;
      max-width: 60vw;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .prerequisites {
      margin-left: 10px;

      .el-tag {
        margin-right: 5px;
        transform: scale(0.9);
      }
    }

    .action-buttons {
      margin-left: auto;

      .el-button {
        margin-left: 5px;
      }
    }
  }
}

/deep/.el-button--success {
  background-color: #6f5af6 !important;
  border-color: #6f5af6 !important;
  position: relative;
}

/deep/.el-button--success:active {
  background-color: #501fe3 !important;
  border-color: #501fe3 !important;
}

.handleBtnCont {
  padding-bottom: 20px;

  /deep/.el-button--success::before {
    content: '';
    width: 60px;
    height: 18px;
    background: url('../../../../assets/exam/corner_mark.png') no-repeat;
    background-size: 100% 100%;
    position: absolute;
    top: -50%;
    right: -50%;
  }
}

.document-ipt-wrapper {
  .left {
    width: 100%;
    margin: 0 auto;
    padding: 20px;

    .upload {
      height: 500px;
      margin: 20px auto;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;
    }
  }

  /* 知识图谱相关样式 */
  .content_container {
    background-color: white;
    width: 100%;
    height: 80vh;
    border-radius: 10px;
    display: flex;
    justify-content: center;
    align-items: center;

    .tree-chart {
      height: 100%;
      width: 100%;
    }
  }

  .buttons-container {
    margin-top: 20px;
    display: flex;
    justify-content: center;

    .documentIpt_btn {
      width: 120px;
      height: 50px;
      // background-color: #b49c73;
      // border-color: #b49c73;
      color: white;
      font-size: 16px;
      font-weight: 700;
      margin: 0 20px;
    }

    .left_btn,
    .right_btn {
      margin: 0 20px;
    }
  }
}

.loadingall {
  display: flex;

  .loadingleft {
    flex: 0 0 40%;
  }

  .loadingright {
    flex: 0 0 60%;
    display: flex;
    justify-content: center;
    align-items: flex-start;
  }
}

.progress-container {
  margin-left: 80px;
  width: 60%;
  height: 20px;
  background-color: #f5f5f5;
  border-radius: 5px;
}

.progress-bar {
  height: 100%;
  background-color: #007bff;
  transition: width 0.5s ease;
}

.progress-text {
  color: white;
  margin-left: 10px;
}

/* 等待知识点抽取相关样式 */
.waiting-container {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  width: 100%;
  padding: 20px;
  box-sizing: border-box;
}

.waiting-content {
  padding: 20px;
  width: 100%;
}

.waiting-header {
  display: flex;
  align-items: center;
  margin-bottom: 20px;

  i {
    font-size: 24px;
    color: #409eff;
    margin-right: 10px;
  }

  h2 {
    font-size: 18px;
    font-weight: 600;
    color: #333;
    margin: 0;
  }
}

.waiting-position {
  margin-bottom: 20px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: start;

  .progress-wrapper {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
  }

  .position-info {
    display: flex;
    align-items: center;
    width: 100%;

    .position-number {
      font-size: 22px;
      font-weight: 700;
      color: #409eff;
      margin-right: 5px;
    }

    .position-text {
      font-size: 14px;
      color: #666;
    }
  }
}

.waiting-time {
  font-size: 14px;
  color: #999;
  text-align: center;
  margin-bottom: 20px;
}

.waiting-tips {
  margin-bottom: 20px;
  background-color: #fff;
  border-radius: 8px;
  padding: 15px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

  h3 {
    font-size: 16px;
    font-weight: 500;
    color: #333;
    margin-bottom: 10px;
  }

  .tip-content {
    width: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;

    p {
      font-size: 14px;
      color: #666;
      margin: 0 0 10px 0;
      text-align: center;
    }

    .tip-controls {
      display: flex;
      align-items: center;
      justify-content: space-between;

      .tip-nav {
        cursor: pointer;
        font-size: 18px;
        color: #409eff;

        &:hover {
          color: #66b1ff;
        }
      }

      .tip-indicator {
        font-size: 14px;
        color: #999;
      }
    }
  }
}

.waiting-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;

  el-button {
    width: 48%;
  }
}
</style>
